class_name CubemapToPanorama

var cubemap: Array = []
var panorama: Image = null
var default_color: Color = Color()
var estimate_color: bool = true

# NOTE: this is tuned for Ballance skybox files
func _tex_cube(dir: Vector3) -> Color:
	var index: int = -1
	var uv: Vector2 = Vector2()
	var abs_dir: Vector3 = dir.abs()

	match abs_dir.max_axis():
		Vector3.AXIS_X:
			index = CubeMap.SIDE_RIGHT if dir.x > 0 else CubeMap.SIDE_LEFT
			uv = Vector2(1.0 - dir.z / dir.x, 1.0 - dir.y / abs_dir.x)
		Vector3.AXIS_Y:
			index = CubeMap.SIDE_TOP if dir.y > 0 else CubeMap.SIDE_BOTTOM
			uv = Vector2(1.0 + dir.x / abs_dir.y, 1.0 + dir.z / dir.y)
		Vector3.AXIS_Z:
			index = CubeMap.SIDE_FRONT if dir.z > 0 else CubeMap.SIDE_BACK
			uv = Vector2(1.0 + dir.x / dir.z, 1.0 - dir.y / abs_dir.z)

	if index >= 0 and index < 6:
		var side: Image = cubemap[index]
		if side != null:
			# TODO: lerp
			return side.get_pixelv(uv * (0.5 * side.get_width()))

	return default_color

func _load_native_ctp():
	return preload("res://gdnative/NativeCubemapToPanorama.gdns").new()

func cubemap_to_panorama(width: int = 2048, height: int = 1024, sun_param = null):
	var native_ctp = _load_native_ctp()

	if native_ctp != null:
		var arr: Array = []

		for i in 6:
			var side: Image = cubemap[i]
			if side != null:
				if side.get_format() != Image.FORMAT_RGB8:
					push_warning("convert the image to FORMAT_RGB8")
					side.convert(Image.FORMAT_RGB8)
				side.lock()
				arr.append(side.get_width())
				arr.append(side.get_height())
				arr.append(side.get_data())
			else:
				arr.append(0)
				arr.append(0)
				arr.append(null)

		arr.append(width)
		arr.append(height)
		arr.append(default_color)
		arr.append(estimate_color)

		if sun_param != null:
			for p in sun_param:
				arr.append(p)

		var pba: PoolByteArray = native_ctp.cubemap_to_panorama(arr)

		panorama = Image.new()
		panorama.create_from_data(width, height, false, Image.FORMAT_RGBE9995, pba)

		for i in 6:
			var side: Image = cubemap[i]
			if side != null:
				side.unlock()

		return

	push_warning("fallback to script mode, make the image size smaller")

	var w: int = width >> 3
	var h: int = height >> 3

	panorama = Image.new()
	panorama.create(w, h, false, Image.FORMAT_RGB8)

	for i in 6:
		var side: Image = cubemap[i]
		if side != null:
			side.lock()

	panorama.lock()

	for y in h:
		var theta: float = (y + 0.5) / h * PI
		var sin_theta: float = sin(theta)
		var cos_theta: float = cos(theta)

		for x in w:
			var phi: float = (x + 0.5) / w * PI * 2.0
			var dir: Vector3 = Vector3(sin_theta * sin(phi), cos_theta, sin_theta * cos(phi))
			panorama.set_pixel(x, y, _tex_cube(dir))

	panorama.unlock()

	for i in 6:
		var side: Image = cubemap[i]
		if side != null:
			side.unlock()

	panorama.resize(width, height, Image.INTERPOLATE_CUBIC)
